//////////////////////////////////////////////////////////////////////////
//
// 2005 by Stefan Hahne (Stefan.Hahne@gmx.de).
// 
// Parts of this program may have a different copyright.
// This library is free software; you can redistribute it and/or modify
// it.
//
// ParFile.cpp:
//   This is a version just to have something to handle parameter files.
//   It is not sophisticated nor is it without errors. I don't want to
//   spend too much efford in it because it will be handled different
//   later. So i leave out the comment handling!
//
//
// XXX Methoden fuer die Konvertierung der rechten Seite (geti, getf,
//    getvec, ...)
//
//////////////////////////////////////////////////////////////////////////
#include "stdafx.h"

#include "ParFile.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>






const char ParFile::PAR_SECTION_DESC_START = '[';
const char ParFile::PAR_SECTION_DESC_END   = ']';
const char ParFile::PAR_COMMENT            = '#';
const char ParFile::PAR_DESCRIPTOR_SEP     = '=';
const char ParFile::PAR_EOLN               = '\n';
const char ParFile::PAR_QUOTES             = '"';






/**
 *
 *
 */
ParFile::ParFile(const char *filename)
{
  assert(filename != NULL);

  m_filename[0] = 0;
  m_pData = NULL;
  m_size = 0;

  m_fLoaded = Load(filename);

  
}


/**
 *
 *
 */
ParFile::~ParFile()
{
  if (m_pData != NULL) { delete[] m_pData; }
}



/**
 * Load the parameter file into memory
 *
 */
bool
ParFile::Load(const char *filename)
{
  FILE   *pFile;
  size_t  nread;

  if (strlen(filename) >= PAR_FILENAME_MAX) { return false; }
  
  pFile = fopen(filename, "rb");
  if (NULL == pFile) { return false; }  // file not found

  fseek(pFile, 0, SEEK_END);
  m_size = ftell(pFile);
  fseek(pFile, 0, SEEK_SET);

  // I will treat the data as one long string so i need 1 char extra for
  // a trailing zero!
  m_pData = new char [m_size+1];
  assert(m_pData != NULL);
  m_pData[m_size] = 0; // !!

  nread = fread(m_pData, 1, m_size, pFile);
  if (nread != m_size)
  {
    delete[] m_pData;
    m_pData = NULL;
    fclose(pFile);
    return false;
  }
  
  fclose(pFile);
 
// this is just in case i want to implement a reload-method some day
  strcpy(m_filename, filename);
  return true;
}




/**
 * 
 * XXX constants
 * XXX case insensitiv
 */
char *
ParFile::SearchSection(const char *section)
{
  static char mask[100];
  int         lentmp;
  
  if (strlen(section) > 97) { return NULL; } // DESC_START+DESC_END+0

// build up the search mask
  mask[0] = PAR_SECTION_DESC_START;
  strcpy(&mask[1], section);
  lentmp = strlen(mask);
  mask[lentmp] = PAR_SECTION_DESC_END;
  mask[lentmp+1] = 0;

  return strstr(m_pData, mask);
}



/**
 * Searches for a descriptor inside a section
 *
 */
char *
ParFile::SearchDescriptor(const char *descriptor, char *sectionstart)
{
  int  i, j;
  int  desclength;
  bool fFound;
  bool fCharMatch;
  
  i = 0;
  while ((sectionstart[i] != PAR_SECTION_DESC_END) &&
         (sectionstart[i] != PAR_EOLN)             &&
         (sectionstart[i] != 0))
  {
    i++;
  }

  if ((0 == sectionstart[i]) || 
      (PAR_EOLN == sectionstart[i]))
  {
    return NULL;
  }


  desclength = strlen(descriptor);
  fFound = false;
  do
  {
    i++;

    if (sectionstart[i] == descriptor[0])
    {
      // compare until difference or match
      j = i;
      fCharMatch = true;
      
      do {
        j++;

        fCharMatch = (sectionstart[j] == descriptor[j-i]);

        if (fCharMatch && ((j-i) == (desclength-1))) { fFound = true; }
      } while (fCharMatch && !fFound);
    }
  } while ((!fFound) &&
           (sectionstart[i] != 0) &&
           (sectionstart[i] != PAR_SECTION_DESC_START));

  return (fFound) ? &sectionstart[i] : NULL;
}



/**
 * returns the value of a descriptor (the string on the right side of the '=')
 *
 */
char *
ParFile::GetValue(const char *section, const char *descriptor)
{
  char *sectionstart;
  char *descstart;
  int   i, j;
  int   vstart, vend;

  assert(section != NULL);
  assert(descriptor != NULL);

  if (!m_fLoaded) { return NULL; }

  sectionstart = SearchSection(section);
  if (NULL == sectionstart) { return NULL; }

  descstart = SearchDescriptor(descriptor, sectionstart);
  if (NULL == descstart) { return NULL; }

  // search for PAR_DESCRIPTOR_SEP
  i = 0;
  do { i++; } while ((descstart[i] != PAR_DESCRIPTOR_SEP) &&
                     (descstart[i] != 0)                  &&
                     (descstart[i] != PAR_EOLN));
  if (descstart[i] != PAR_DESCRIPTOR_SEP) { return NULL; }

  
  // get the value
  vstart = i+1;
  j = vstart; 
  while ((descstart[j] != 0)          &&
         (descstart[j] != PAR_EOLN)   &&
         (descstart[j] != PAR_COMMENT))
  {
    j++;
  }

  vend = j-1;

  // wenn kein Value vorhanden ist (j == vstart)  oder
  // der Value zu gross ist (>= PAR_DESCRIPTOR_VALUE_MAX-1)
  if ((j == vstart) ||
      ((vend - vstart + 1) >= PAR_DESCRIPTOR_VALUE_MAX-1))
  {
    return NULL;
  }
  

  memcpy(m_descvalue, &descstart[vstart], vend - vstart + 1);
  m_descvalue[vend - vstart + 1] = 0;

  return m_descvalue;
}




/**
 * Schneidet fuehrenden und abschliessende Leerzeichen ab.
 *
 */
char *
ParFile::CutSpace(char *value)
{
  char *pc;

  if (NULL == value) { return NULL; }
  
// Step 1: suche das erste nicht-space Zeichen
  pc = value;
  while ((*pc != 0) && isspace(*pc)) { pc++; }

// Step 2: shifte den gesamten String nach links an den Anfang
  strcpy(value, pc);

// Step 3: das erste nicht Leerzeichen von der rechten Seite aus gesehen ist
//         das neue Ende
  pc = &value[strlen(value)-1];
  while ((pc != value) && isspace(*pc)) { pc--; }
  pc[1] = 0;

  return value;
}













